//
// Created by hjie on 23-9-24.
//

#include "stun_server_manager.h"
#include "stun_server/stun_server.h"
#include "stun_udp_server.h"
#include "./../message_process.h"
#include <cstdio>
#include <memory>
#include <mutex>
#include <netinet/in.h>

StunServerManager::StunServerManager(MessageProcess *message_process, const std::string & public_ip)
{
    m_message_process = message_process;
    m_public_ip       = public_ip;
}

std::shared_ptr<StunUdpServer> StunServerManager::CreateUdpServer(const std::string & nonce_data)
{
    auto server = FindStunServerByNonceId(nonce_data);
    if (server)
    {
        server->m_refresh_time = time(nullptr);
        return server;
    }
    auto udp_server = std::make_shared<StunUdpServer>(m_message_process->m_loop_event, this);
    udp_server->SetPublicIp(m_public_ip);
    if (!udp_server->CreateUdpServer())
    {
        udp_server = nullptr;
    }
    else
    {
        udp_server->m_connection_nonce = nonce_data;
        m_stun_udp_server_map[ntohs(udp_server->m_server_sai.sin_port)] = udp_server;
        m_nonce_port_map[nonce_data] = ntohs(udp_server->m_server_sai.sin_port);
    }
    return udp_server;
}

std::shared_ptr<StunUdpServer> StunServerManager::FindStunServerByNonceId(const std::string &nonce_id)
{
    auto port_iter = m_nonce_port_map.find(nonce_id);
    if (port_iter != m_nonce_port_map.end())
    {
        auto server_iter = m_stun_udp_server_map.find(port_iter->second);
        if (server_iter != m_stun_udp_server_map.end())
        {
            return server_iter->second;
        }
        else
        {
            return nullptr;
        }
    }
    else
    {
        return nullptr;
    }
}

std::shared_ptr<StunUdpServer> StunServerManager::FindStunServerByAddress(AttrXorMappedAddress *address)
{
    uint16_t port = htons(address->LocalPort());
    std::string addr = address->LocalIp();

    for (auto & data : m_stun_udp_server_map)
    {
        char buff[128] = {0};
        inet_ntop(data.second->m_allocator_sai.sin_family, &data.second->m_allocator_sai.sin_addr.s_addr, buff, 128);
    
        printf("allocate data port : %d and server ip : %s.\n", data.second->m_allocator_sai.sin_port, buff);

        if (data.second->m_allocator_sai.sin_port == port)
        {
            if (addr.compare(buff) == 0)
            {
                return data.second;
            }
        }
    }
    return nullptr;
}


void StunServerManager::ProcessMessage(StunUdpServer *server, StunMessage &message)
{
    m_message_process->ProcessMsgFromUdpServer(server, message);
}

void StunServerManager::CheckRelayServerExpire()
{
    uint64_t now_time = time(nullptr);

    std::unique_lock<std::mutex> lk(m_mtx);

    for (auto iter = m_stun_udp_server_map.begin(); iter != m_stun_udp_server_map.end(); )
    {
        auto use_iter = iter++;
        if ((now_time - use_iter->second->m_refresh_time) >= 30)
        {
            m_stun_udp_server_map.erase(use_iter);
        }
    }
    printf("relay resource size : %ld.\n", m_stun_udp_server_map.size());
}

std::shared_ptr<StunUdpServer> StunServerManager::FindStunServerByPort(uint16_t server_port)
{
    std::unique_lock<std::mutex> lk(m_mtx);
    auto iter = m_stun_udp_server_map.find(server_port);
    return iter != m_stun_udp_server_map.end() ? iter->second : nullptr;
}

uint16_t StunServerManager::GetServerPortByNonceId(const std::string & nonce_id)
{
    std::unique_lock<std::mutex> lk(m_mtx);
    auto iter = m_nonce_port_map.find(nonce_id);
    return iter != m_nonce_port_map.end() ? iter->second : 0;
}